#pragma once

#include <cassert>
#include <cstdint>
#include <cstring>
#include <utility>

/**
 * 缓冲区类
 *
 * buffer_ptr_                        buffer_size_
 *   |                                      |
 *   v                                      V
 *   +----+----------------+----------------+
 *   |    | readable bytes | writable bytes |
 *   +----+----------------+----------------+
 *        ^                ^
 *        |                |
 *    read_index_       write_index_
 *
 * 使用示例：
 *  Buffer b;
 *  b.append("abc", 4);  //! 往缓冲中写入4字节的数据
 *  char buff[10];
 *  b.fetch(buff, 4);    //! 从缓冲中取出4字节的数据
 *
 *  b.ensureWritableSize(10);            //! 预留10个字节
 *  memset(b.writableBegin(), 0xcc, 10); //! 将该10个字节全置为0xcc
 *  b.hasWritten(10);                    //! 标该已写入10个字节
 *
 * \warnning    多线程使用需在外部加锁
 */

class Buffer
{
public:
    static constexpr std::size_t kInitialSize = 256;

    explicit Buffer(std::size_t reverse_size = kInitialSize)
    {
        //! 如果预留空间大小，那么要为缓冲分配空间
        if (reverse_size > 0)
        {
            uint8_t *p_buff = new uint8_t[reverse_size];
            assert(p_buff != nullptr);
            buffer_ptr_ = p_buff;
            buffer_size_ = reverse_size;
        }
    }

    virtual ~Buffer()
    {
        if (buffer_ptr_ != nullptr)
        {
            delete[] buffer_ptr_;
            buffer_ptr_ = nullptr;
        }
    }

    Buffer(const Buffer &other)
    {
        cloneFrom(other);
    }

    Buffer(Buffer &&other)
    {
        swap(other);
    }

    Buffer &operator=(const Buffer &other)
    {
        {
            if (this != &other)
                cloneFrom(other);

            return *this;
        }
    }

    Buffer &operator=(Buffer &&other)
    {
        if (this != &other)
        {
            reset();
            swap(other);
        }

        return *this;
    }

    void swap(Buffer &other)
    {
        if (&other == this)
            return;

        std::swap(other.buffer_ptr_, buffer_ptr_);
        std::swap(other.buffer_size_, buffer_size_);
        std::swap(other.read_index_, read_index_);
        std::swap(other.write_index_, write_index_);
    }

    void reset()
    {
        Buffer tmp(0);
        swap(tmp);
    }

public:
    /**
     * 写缓冲操作
     */

    //! 获取可写空间大小
    inline std::size_t writableSize() const
    {
        return buffer_size_ - write_index_;
    }

    //! 保障有指定容量的可写空间
    bool ensureWritableSize(std::size_t write_size)
    {
        if (write_size == 0)
            return true;

        //! 空间足够
        if (writableSize() >= write_size)
            return true;

        //! 检查是否可以通过将数据往前挪是否可以满足空间要求
        if ((writableSize() + read_index_) >= write_size)
        {
            //! 将 readable 区的数据往前移
            ::memmove(buffer_ptr_, (buffer_ptr_ + read_index_), (write_index_ - read_index_));
            write_index_ -= read_index_;
            read_index_ = 0;
            return true;
        }
        else
        {                                                            //! 只有重新分配更多的空间才可以
            std::size_t new_size = (write_index_ + write_size) << 1; //! 两倍扩展
            uint8_t *p_buff = new uint8_t[new_size];
            if (p_buff == nullptr)
                return false;

            if (buffer_ptr_ != nullptr)
            {
                //! 只需要复制 readable 部分数据
                ::memcpy((p_buff + read_index_), (buffer_ptr_ + read_index_), (write_index_ - read_index_));
                delete[] buffer_ptr_;
            }

            buffer_ptr_ = p_buff;
            buffer_size_ = new_size;

            return true;
        }
    }

    //! 获取写首地址
    inline uint8_t *writableBegin() const
    {
        return (buffer_ptr_ != nullptr) ? (buffer_ptr_ + write_index_) : nullptr;
    }

    //! 标记已写入数据大小
    void hasWritten(std::size_t write_size)
    {
        if (write_index_ + write_size > buffer_size_)
        {
            write_index_ = buffer_size_;
        }
        else
        {
            write_index_ += write_size;
        }
    }

    //! 往缓冲区追加指定的数据块，返回实现写入的大小
    std::size_t append(const void *p_data, std::size_t data_size)
    {
        if (ensureWritableSize(data_size))
        {
            ::memcpy(writableBegin(), p_data, data_size);
            hasWritten(data_size);
            return data_size;
        }
        return 0;
    }

    /**
     * 读缓冲操作
     */

    //! 获取可读区域大小
    inline std::size_t readableSize() const
    {
        return write_index_ - read_index_;
    }

    //! 获取可读区首地址
    inline uint8_t *readableBegin() const
    {
        return (buffer_ptr_ != nullptr) ? (buffer_ptr_ + read_index_) : nullptr;
    }

    //! 标记已读数据大小
    void hasRead(std::size_t read_size)
    {
        if (read_index_ + read_size > write_index_)
        {
            read_index_ = write_index_ = 0;
        }
        else
        {
            read_index_ += read_size;
            if (read_index_ == write_index_)
                read_index_ = write_index_ = 0;
        }
    }

    //! 标记已读取全部数据
    void hasReadAll()
    {
        read_index_ = write_index_ = 0;
    }

    //! 从缓冲区读取指定的数据块，返回实际读到的数据大小
    std::size_t fetch(void *p_buff, std::size_t buff_size)
    {
        size_t read_size = (buff_size > readableSize()) ? readableSize() : buff_size;
        ::memcpy(p_buff, readableBegin(), read_size);
        hasRead(read_size);
        return read_size;
    }

    /**
     * 其它
     */
    //! 缩减缓冲多余容量
    void shrink()
    {
        Buffer tmp(*this); //! 将自己复制给 tmp，其间会缩减空间
        swap(tmp);         //! 与 tmp 交换
    }

protected:
    void cloneFrom(const Buffer &other)
    {
        if (buffer_ptr_ != nullptr)
        {
            delete[] buffer_ptr_;
            buffer_ptr_ = nullptr;
        }

        //! 如果 other 有可读数据，则要根据可读大小分配空间
        if (other.readableSize() > 0)
        {
            uint8_t *p_buff = new uint8_t[other.readableSize()];
            assert(p_buff != nullptr);
            ::memcpy(p_buff, other.readableBegin(), other.readableSize());
            buffer_ptr_ = p_buff;
            buffer_size_ = write_index_ = other.readableSize();
        }
        else
        {
            buffer_ptr_ = nullptr;
            buffer_size_ = write_index_ = 0;
        }

        read_index_ = 0;
    }

private:
    uint8_t *buffer_ptr_ = nullptr; //! 缓冲区地址
    std::size_t buffer_size_ = 0;   //! 缓冲区大小

    std::size_t read_index_ = 0;  //! 读位置偏移
    std::size_t write_index_ = 0; //! 写位置偏移
};
